package cn.shoptnt.framework.validation.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.shoptnt.framework.validation.annotation.SafeDomain;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义注解 @SafeDomain实现类
 *
 * @author LiuXT
 */
public class SafeDomainValidator implements ConstraintValidator<SafeDomain, Object> {

    //域名白名单
    public static List<String> urlWhiteList;

    public static void setUrlWhiteList(List<String> urlWhiteList) {
        SafeDomainValidator.urlWhiteList = urlWhiteList;
    }

    /**
     * SafeDomain-正则表达式校验 pattern
     * 含义：http://或 https://开头或没有开头(意味着以域名本身开头)，至第一个"/"之间的内容则为域名
     * 如：http://www.a.com/xx/xx.jpg中，group(2)将获取到 "www.a.com"
     */
    public static Pattern safeDomainPattern = Pattern.compile("(http://|https://|^)(.*?)/");

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        return checkDomainParam(value);
    }

    @Override
    public void initialize(SafeDomain constraintAnnotation) {

    }

    /**
     * Object转换为 List
     *
     * @param obj 目标参数
     * @param clazz 指定参数类型
     * @param <T>
     * @return 返回指定类型数据的集合
     */
    public static <T> List<T> objCastList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<T>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
        }
        return result;
    }

    /**
     * 根据白名单校验域名 - String或 List<String>类型参数
     * 对参数进行类别区分，然后获取到各个参数中域名部分，对其进行校验，在白名单内则合法，否则不合法
     *
     * @param value 校验目标参数（String或 List<String>类型）
     * @return 参数内域名部分合法返回true，不合法返回false
     */
    public static boolean checkDomainParam(Object value) {
        // 当白名单以及参数 都不为空时，进行校验；而都为空时直接放行
        if (ObjectUtil.isNotEmpty(urlWhiteList) && ObjectUtil.isNotEmpty(value)) {
            // 当参数类型为 String时
            if (value instanceof String) {
                return checkContainsValue(String.valueOf(value));

                // 当参数类型为 List<String>时
            } else if (value instanceof List) {
                // 先将参数转为List<String>
                List<String> valueList = objCastList(value, String.class);
                // 根据转换结果判断，不为空则进行校验；否则不进行校验
                if (ObjectUtil.isNotEmpty(valueList)) {
                    // 转换成功，遍历集合
                    for (String param : valueList) {
                        return checkContainsValue(param);
                    }
                }
            }
        }
        return true;
    }

    /**
     * 校验域名参数合法性
     * 获取到参数中域名部分，对其进行校验，在白名单内则合法，否则不合法
     *
     * @param param 校验目标参数，对String类型参数进行正则校验
     * @return 参数内域名部分合法返回true，不合法返回false
     */
    public static boolean checkContainsValue(String param) {
        // 开始校验
        Matcher matcher = safeDomainPattern.matcher(param);
        // 根据表达式，获取到参数中的域名部分，若白名单包含则表示校验成功，否则校验失败
        boolean whetherFind = matcher.find();
        if (whetherFind) {
            String group = matcher.group(2);
            return urlWhiteList.contains(group);
        }
        return true;
    }
}
